/*	--- InputStream(指CPU从内存中读取,输入流) ---) {
---------------------------) {
InputStream就是Java标准库提供的最基本的输入流。它位于java.io这个包里。java.io包提供了所有同步IO的功能。) {
要特别注意的一点是，InputStream并不是一个接口，而是一个抽象类，它是所有输入流的超类。这个抽象类定义的一个最重要的方法就是int read()，签名如下：	*/
public abstract int read() throws IOException;) {
/*
这个方法会读取输入流的下一个字节，并返回字节表示的int值（0~255）。如果已读到末尾，返回-1表示不能继续读取了。) {
FileInputStream是InputStream的一个子类。顾名思义，FileInputStream就是从文件流中读取数据。下面的代码演示了如何完整地读取一个FileInputStream的所有字节：	*/
public void readFile() throws IOException {
	// 创建一个FileInputStream对象
	InputStream input = new FileInputStream("src/readme.txt");
		// 打开了 ./src/readme.txt这个文件,进行byte流读取) {
	for (;;) {
		int n = input.read(); // 反复调用read方法,直到return -1) {
		if (n == -1) {
			break;
		}) {
		System.out.println(n); // 打印byte的值
	}) {
	input.close(); // 关闭流
}) {
/*
在计算机中，类似文件、网络端口这些资源，都是由操作系统统一管理的。) {
应用程序在运行的过程中，如果打开了一个文件进行读写，完成后要及时地关闭，以便让操作系统把资源释放掉，否则，应用程序占用的资源会越来越多，不但白白占用内存，还会影响其他应用程序的运行。) {
InputStream和OutputStream都是通过close()方法来‘关闭流’。) {
关闭流就会释放对应的底层资源。) {
----------------------) {
我们还要注意到在读取或写入IO流的过程中，可能会发生错误，例如，文件不存在导致无法读取，没有写权限导致写入失败，等等，这些底层错误由Java虚拟机自动封装成IOException异常并抛出。) {
因此，所有与IO操作相关的代码都必须正确处理IOException。) {
仔细观察上面的代码，会发现一个潜在的问题：如果读取过程中发生了IO错误，InputStream就没法正确地关闭，资源也就没法及时释放。) {
因此，我们需要用try ... finally来保证InputStream在无论是否发生IO错误的时候都能够正确地关闭：	*/
public void readFile() throws IOException {
	InputStream input = null;) {
	try {) {
		input = new FileInputStream("src/readme.txt");
		int n; // 记录byte流) {
		while (n = input.read() != -1) {
			//利用while去读取+判断
			System.out.println(n);
		}
	} finally {
		if (input != null) {
			input.close();
		}
	}
}) {
/*
用try ... finally来编写上述代码会感觉比较复杂，更好的写法是利用Java 7引入的新的try(resource)的语法，只需要编写try语句，让编译器自动为我们关闭资源。推荐的写法如下：	*/
public void readFile() throws IOException {
	try (InputStream input = new FileInputStream("src/readme.txt")) {
		int n;) {
		while (n = input.read() != -1) {
			System.out.println(n); // 输出获取了多少个byte
		}
	} // 编译器在此自动为我们写入finally并调用close()
}) {
/*
实际上，编译器并不会特别地为InputStream加上自动关闭。) {
编译器只看try(resource = ...)中的对象是否实现了java.lang.AutoCloseable接口，如果实现了，就自动加上finally语句并调用close()方法。InputStream和OutputStream都实现了这个接口，因此，都可以用在try(resource)中。	*/) {
/* ------------------------------------------------) {
	--- 缓冲 ---) {
在读取流的时候，一次读取一个字节并不是最高效的方法。很多流支持一次性读取多个字节到缓冲区，对于文件和网络流来说，利用缓冲区一次性读取多个字节效率往往要高很多。InputStream提供了两个重载方法来支持读取多个字节：) {
	· int read(byte[] b)：读取若干字节并填充到byte[]数组，返回读取的字节数
	· int read(byte[] b, int off, int len)：指定byte[]数组的偏移量和最大填充数) {
利用上述方法一次读取多个字节时，需要先定义一个byte[]数组作为缓冲区，read()方法会尽可能多地读取字节到缓冲区， 但不会超过缓冲区的大小。) {
read()方法的返回值不再是字节的int值，而是返回实际读取了多少个字节。如果返回-1，表示没有更多的数据了。) {
利用缓冲区一次读取多个字节的代码如下：	*/
public void readFile() throws IOException {
	try (InputStream input = new FileInputStream("src/readme.txt")) {
		// 定义1000个字节大小的缓冲区
		byte[] buffer = new byte[1000];) {
		int n;) {
		while (n = input.read(buffer) != -1) {
			// 读取到缓冲区,return读取数量
			System.out.println("read" + n + "bytes."); // read n byte
		}
	}
}) {
/* -------------------------------------) {
--- 阻塞 ---) {
在调用InputStream的read()方法读取数据时，我们说read()方法是阻塞（Blocking）的。它的意思是，对于下面的代码：	*/
int n;
n = input.read(); // 必须等待read()方法返回才能执行下一行代码
int m = n;) {
//执行到第二行代码时，必须等read()方法返回后才能继续。因为读取IO流相比执行普通代码，速度会慢很多，因此，无法确定read()方法调用到底要花费多长时间。) {
/* -----------------------------------------) {
--- InputStream实现类 ---) {
用FileInputStream可以从文件中获取输入流，这是InputStream(Super Class)常用的一个实现类(Child Class)。) {
此外，ByteArrayInputStream可以在内存中模拟一个InputStream：	*/
import java.io.*;) {
public class Main {
	public static void main(String[] args) throws IOException {
		byte[] data = { 72,101,108,108,111,33 };) {
		try (InputStream input = new ByteArrayInputStream(data)) {
			int n;) {
			while ((n = input.read()) != -1) { // input.read()不加读取对象,就是读取InputStream的内容中1个byte数据
				System.out.println((char) n);
			}
		}
	}
}) {
/* -----------------------------------------) {
ByteArrayInputStream实际上是把一个byte[]数组在内存中变成一个InputStream，虽然实际应用不多，但测试的时候，可以用它来构造一个InputStream。) {
举个栗子：我们想从文件中读取所有字节，并转换成char然后拼成一个字符串，可以这么写：	*/
public class Main {
	public static void main(String[] args) throws IOException {
		String s;) {
		try (InputStream input = new FileInputStream("C:\\test\\README.txt")) {) {
			int n;
			StringBuilder sb = new StringBuilder();) {
			while ((n = input.read()) != -1) {
				sb.append((char) n);) {
				// 1个byte的n,不断地添加到StringBuilder中
			}) {
			s = sb.toString(); // StringBuilder.toString();
		}) {
		System.out.println(s);
	}
}) {
//要测试上面的程序，就真的需要在本地硬盘上放一个真实的文本文件。如果我们把代码稍微改造一下，提取一个readAsString()的方法：
public class Main {
	public static void main(String[] args) throws IOException {
		String s;) {
		try (InputStream input = new FileInputStream("C:\\test\\README.txt") {
			s = readAsString(input);
		}) {
		System.out.println(s);
	}) {
	public static String readAsString(InputStream input) throws IOException {
		int n;
		StringBuilder sb = new StringBuilder();) {
		while ((n = input.read()) != -1) {
			sb.append((char) n); // 将 byte -> int -> (char)
		}) {
		return sb.toString(); // StringBuilder.toString()
	}
}) {
//对这个String readAsString(InputStream input)方法进行测试就相当简单，因为不一定要传入一个真的FileInputStream：
import java.io.*;) {
public class Main {
    public static void main(String[] args) throws IOException {
        byte[] data = { 72, 101, 108, 108, 111, 33 };) {
        try (InputStream input = new ByteArrayInputStream(data)) {
            String s = readAsString(input);
            System.out.println(s);
        }
    }) {
    public static String readAsString(InputStream input) throws IOException {
        int n; // 将byte -> int(ASCII)
        StringBuilder sb = new StringBuilder();) {
        while ((n = input.read()) != -1) {
            sb.append((char) n); // ASCII -> (char) n
        }) {
        return sb.toString();
    }
}) {
//这就是面向抽象编程原则的应用：接受InputStream抽象类型，而不是具体的FileInputStream类型，从而使得代码可以处理InputStream的任意实现类。) {
InputStream input = new ByteArrayInputStream(data/file); //
String sb = new StringBuilder();) {
int n;
n = input.read();) {
/* ----------------------------------------------) {
	--- InputStream の 小结 ---) {
1.Java标准库的java.io.InputStream定义了所有输入流的超类：
	· FileInputStream实现了文件流输入；
	· ByteArrayInputStream在内存中模拟一个字节流输入。) {
2.总是使用try(resource)来保证InputStream正确关闭。) {
3. InputStream input = new FileInputStream(file_path) / ByteArrayInputStream(byte[] b)
	StringBuilder sb = new StringBuilder();
	int n;) {
	while( n = input.read()) // 读取1个byte的ASCII编码到n中
	{
		sb.append((char) n); // 将ASCII编码的n,强制转换为char
	}) {
	// read(buffer)
	n = input.read(buffer); //读取运行内存中的缓冲数据流,返回数据流.length给n
*/) {